"""
时间复杂度：
    概述：
        时间复杂度衡量的是一个算法执行所需要的时间随着输入数据的大小（通常用N表示）增长而增长的量级或者趋势。
    白话：
        它关注的不是精确的执行时间（如：多少秒），而是当输入数据规模N（例如数组的长度，元素的个数，循环的层数）变得非常大时，算法运行时间的增长率，
        比如：
            1：当N=100时，算法运行时间为100ms
            2：当N=1000时，算法运行时间为1000ms
            3：当N=10000时，算法运行时间为10000ms
    为什么重要：
        1：它可以帮助我们评估算法的效率，选择更合适的算法。
        2：它可以帮助我们分析算法的性能，预测算法在不同输入规模下的运行时间。
    如何表示？大O表示法
        最常见的表示法就是大O表示法。
            O(...)定义了算法执行时间（或者操作步骤）的一个上界（Upper Bound）描述的是最坏情况下增长率的上限，换句话说：告诉我们，执行时间最多增长多快
        核心思想：忽略常量和低阶项，在分析复杂度的时候，我们只关注主导项（最高阶项），因为当n变得非常大时：
            常数因子的影响会变得微乎其微
            低阶项的增长速度会远低于最高阶项
        例子：
            如果一个算法实际上需要3N ** 2 + 100N + 50步操作。
            TODO 我们会忽略常数：3、100、50
            再忽略低阶项：N
            最后保留最高阶项：N ** 2
    分析时的注意事项：
        1：输入规模（N）的定义：需要明确定义N指的是什么（数组的长度，元素的个数，循环的层数）
        2：最好、平均、最坏：
            最好情况：输入数据已经有序，或者已经满足某种条件，算法的运行时间最短。
            平均情况：输入数据是随机的，算法的运行时间是所有可能输入情况的平均值。
            最坏情况：输入数据是逆序的，或者是最坏的情况，算法的运行时间最长。
    最常用的时间复杂度（TODO从快到慢）：
        O(1)：常数时间复杂度，算法的运行时间不随着输入数据的大小而变化。（不论输入数据多大，操作次数是固定的）
            例如：
                访问数组中的一个元素
                进行一次简单的加减乘除运算
                调用一个函数
        O(logN)：对数时间复杂度，算法的运行时间随着输入数据的大小以对数的速度增长。
            例如：
                二分查找
                快速排序
        O(N)：线性时间复杂度，算法的运行时间随着输入数据的大小以线性的速度增长。
            例如：
                遍历数组
                遍历链表
        O(NlogN)：线性对数时间复杂度，算法的运行时间随着输入数据的大小以线性对数的速度增长。
            例如：
                归并排序
                快速排序
        O(N**2)：平方时间复杂度，算法的运行时间随着输入数据的大小以平方的速度增长。
            例如：
                未优化的选择排序、插入排序
                冒泡排序
        O(2**N)：指数时间复杂度，算法的运行时间随着输入数据的大小以指数的速度增长。
            例如：
                暴力破解密码（穷举所有组合）、求解旅行商问题
        O(N!)：阶乘时间复杂度，算法的运行时间随着输入数据的大小以阶乘的速度增长。
            例如：
                旅行商问题（TSP）的暴力求解算法
"""